package cn.turboinfo.fuyang.api.provider.common.service.impl.shop;

import cn.turboinfo.fuyang.api.domain.common.service.product.ProductService;
import cn.turboinfo.fuyang.api.domain.common.service.shop.HousekeepingShopService;
import cn.turboinfo.fuyang.api.entity.common.enumeration.shop.ShopStatus;
import cn.turboinfo.fuyang.api.entity.common.pojo.product.Product;
import cn.turboinfo.fuyang.api.entity.common.pojo.shop.HousekeepingShop;
import cn.turboinfo.fuyang.api.entity.common.pojo.shop.HousekeepingShopCreator;
import cn.turboinfo.fuyang.api.entity.common.pojo.shop.QHousekeepingShop;
import cn.turboinfo.fuyang.api.provider.common.constant.GeoConstants;
import cn.turboinfo.fuyang.api.provider.common.repository.database.shop.HousekeepingShopDAO;
import cn.turboinfo.fuyang.api.provider.common.repository.database.shop.HousekeepingShopPO;
import cn.turboinfo.fuyang.api.provider.common.service.geo.GeoDistance;
import cn.turboinfo.fuyang.api.provider.common.service.geo.GeoService;
import kotlin.Pair;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import net.sunshow.toolkit.core.qbean.helper.component.request.QBeanCreatorHelper;
import net.sunshow.toolkit.core.qbean.helper.service.impl.DefaultQServiceImpl;
import nxcloud.foundation.core.data.support.annotation.EnableSoftDelete;
import nxcloud.foundation.core.idgenerator.IdGeneratorFacade;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Sort;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

@Slf4j
@RequiredArgsConstructor
@Service
@EnableSoftDelete
public class HousekeepingShopServiceImpl extends DefaultQServiceImpl<HousekeepingShop, Long, HousekeepingShopPO, HousekeepingShopDAO> implements HousekeepingShopService {

    private final GeoService geoService;

    private final ProductService productService;

    private final IdGeneratorFacade<Long> idGeneratorFacade;

    @Override
    @Transactional
    public HousekeepingShop save(HousekeepingShopCreator creator) {
        HousekeepingShopPO housekeepingShopPO = new HousekeepingShopPO();
        QBeanCreatorHelper.copyCreatorField(housekeepingShopPO, creator);

        // 手动生成ID
        housekeepingShopPO.setId(idGeneratorFacade.nextId());
        return convertQBean(dao.save(housekeepingShopPO));
    }

    @Override
    public Optional<HousekeepingShop> findByCompanyIdAndName(Long companyId, String name) {
        return dao.findByCompanyIdAndName(companyId, name).map(this::convertQBean);
    }

    @Override
    public List<HousekeepingShop> findByCompanyId(Long companyId) {
        return convertStreamQBeanToList(dao.findByCompanyId(companyId).stream());
    }

    @Override
    public List<Pair<HousekeepingShop, BigDecimal>> findByGeoRadius(BigDecimal longitude, BigDecimal latitude, Double radiusInKilometers, Long limit) {
        List<GeoDistance> distanceList = geoService.searchRadius(
                GeoConstants.STORE_SHOP,
                longitude.doubleValue(),
                latitude.doubleValue(),
                radiusInKilometers,
                limit,
                false
        );
        Set<Long> shopIdSet = distanceList.stream()
                .map(GeoDistance::getMember)
                .map(Long::valueOf)
                .collect(Collectors.toSet());
        Map<String, HousekeepingShop> shopMap = findByIdCollection(shopIdSet).stream()
                .collect(
                        Collectors.toMap(v -> v.getId().toString(), Function.identity())
                );

        return distanceList.stream()
                .filter(p -> shopMap.containsKey(p.getMember()))
                .map(p -> new Pair<>(shopMap.get(p.getMember()), BigDecimal.valueOf(p.getDistance())))
                .collect(Collectors.toList());
    }

    @Override
    @Transactional
    public void updateCreditScore(Long id, BigDecimal creditScore) {
        HousekeepingShopPO po = getEntityWithNullCheckForUpdate(id);
        po.setCreditScore(creditScore);
    }

    @Override
    public List<HousekeepingShop> findTopByCreditScore(int count) {
        return convertStreamQBeanToList(
                dao.findAll(
                        PageRequest.of(
                                0, count,
                                Sort.by(
                                        Sort.Order.desc(QHousekeepingShop.creditScore),
                                        Sort.Order.desc(QHousekeepingShop.createdTime)
                                )
                        )
                ).stream()
        );
    }

    @Override
    @Transactional
    public void updateOrderNum(Long id, Long orderNum) {
        HousekeepingShopPO po = getEntityWithNullCheckForUpdate(id);
        po.setOrderNum(orderNum);
    }

    @Override
    public List<HousekeepingShop> findByCityCode(Long cityCode) {
        return convertQBeanToList(dao.findByCityCode(cityCode));
    }

    @Override
    public List<HousekeepingShop> findByAreaCode(Long areaCode) {
        return convertQBeanToList(dao.findByAreaCode(areaCode));
    }

    @Override
    public Map<Long, Set<Long>> batchFindShopCategory(Collection<Long> shopIdCollection) {
        // 通过门店产品涉及的分类倒推门店的分类

        // 先取出门店所有产品
        List<Product> productList = productService.findByShopIdCollection(shopIdCollection);
        return productList.stream()
                .collect(
                        Collectors.groupingBy(
                                Product::getShopId,
                                Collectors.mapping(Product::getCategoryId, Collectors.toSet())
                        )
                );
    }

    @Override
    public List<HousekeepingShop> findByNameContaining(String name) {
        return convertStreamQBeanToList(dao.findByNameContaining(name).stream());
    }

    @Override
    public long countByCompanyId(Long companyId) {
        return dao.countByCompanyIdAndStatus(companyId, ShopStatus.REVIEWED);
    }
}
